package com.yifeng.repo.controller.postman.work;

import com.yifeng.repo.base.utils.common.BaseUtil;
import com.yifeng.repo.base.utils.common.FirePreconditions;
import com.yifeng.repo.controller.postman.configure.PostmanProperties;
import com.yifeng.repo.controller.postman.work.biz.BizCallbackAction;
import com.yifeng.repo.controller.postman.work.biz.BizRequestAction;
import com.yifeng.repo.controller.postman.work.dto.HttpRequestParam;
import com.yifeng.repo.controller.postman.work.dto.PostTaskConf;
import com.yifeng.repo.controller.postman.work.task.HttpCallbackTask;
import com.yifeng.repo.controller.postman.work.task.HttpRequestTask;
import com.yifeng.repo.controller.postman.work.task.handler.PostInterceptor;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Created by daibing on 2023/5/10.
 */
@Slf4j
public class PostmanManager {
    private final ConcurrentMap<String, BizCallbackAction> uri2BizCallbackAction = new ConcurrentHashMap<>();
    private final ConcurrentMap<String, PostInterceptor> clazz2PostInterceptor = new ConcurrentHashMap<>();
    private final String applicationName;
    private final PostmanProperties properties;

    public PostmanManager(String applicationName, PostmanProperties properties) {
        this.applicationName = applicationName;
        this.properties = properties;
    }

    public void init() {
        log.info("yfcloud controller postman init success: OWNER_ID={}, OWNER_NAME={}", BaseUtil.OWNER_ID, BaseUtil.OWNER_NAME);
    }

    public void destroy() {
        log.info("yfcloud controller postman destroy success: OWNER_ID={}, OWNER_NAME={}", BaseUtil.OWNER_ID, BaseUtil.OWNER_NAME);
    }

    /**
     * 发起投递操作
     * @param requestId 请求id
     * @param httpRequestParam 业务投递参数（带占位符），变量类似 {{AccessKeyId.DATA}}，动作类似 {{Timestamp.ACTION}}
     * @param bizSystemParams 业务系统参数（替换占位符变量的参数值）
     * @param bizRequestAction 业务投递动作
     * @return 业务数据对象
     */
    public <T> T doPostWork(String requestId, HttpRequestParam httpRequestParam, Map<String, Object> bizSystemParams, BizRequestAction<T> bizRequestAction) {
        // 1. 检查入参
        FirePreconditions.hasText(requestId, "请求id不能为空");
        FirePreconditions.checkNotNull(httpRequestParam, "业务投递参数不能为Null");
        FirePreconditions.checkNotNull(bizRequestAction, "业务投递动作不能为Null");
        if (bizSystemParams == null) {
            bizSystemParams = Collections.emptyMap();
        }
        if (httpRequestParam.getHeader() == null) {
            httpRequestParam.setHeader(Collections.emptyMap());
        }

        // 2. 执行post http task
        PostTaskConf taskConf = bizRequestAction.getPostTaskConf(properties);
        HttpRequestTask<T> httpRequestTask = new HttpRequestTask<>(
                requestId,
                taskConf,
                buildPostInterceptor(taskConf.getPostInterceptorHandler()),
                httpRequestParam,
                bizSystemParams,
                bizRequestAction);
        return httpRequestTask.doRun();
    }

    /**
     * 处理回调操作
     * @param callbackParam 业务回调参数
     * @return 回调信息
     */
    public String doCallbackWork(HttpRequestParam callbackParam) {
        // 1. 检查入参
        FirePreconditions.checkNotNull(callbackParam, "回调参数不能为Null");
        FirePreconditions.checkNotNull(callbackParam.getHeader(), "回调请求头参数不能为空");
        FirePreconditions.hasText(callbackParam.getBody(), "回调请求体参数不能为空");

        // 2. 获取业务回调动作
        BizCallbackAction bizCallbackAction = uri2BizCallbackAction.get(callbackParam.getUri());
        FirePreconditions.checkNotNull(bizCallbackAction, "业务回调动作不能为Null");

        // 3. 执行callback http task
        HttpCallbackTask httpCallbackTask = new HttpCallbackTask(callbackParam, bizCallbackAction);
        return httpCallbackTask.doRun();
    }

    public void addBizCallbackAction(BizCallbackAction bizCallbackAction) {
        String uri = "/" + applicationName + properties.getCallbackPrefix() + bizCallbackAction.tag();
        uri2BizCallbackAction.put(uri, bizCallbackAction);
    }

    public void removeBizCallbackAction(String tag) {
        String uri = "/" + applicationName + properties.getCallbackPrefix() + tag;
        uri2BizCallbackAction.remove(uri);
    }

    private PostInterceptor buildPostInterceptor(Class<? extends PostInterceptor> postInterceptorHandler) {
        PostInterceptor postInterceptor = clazz2PostInterceptor.get(postInterceptorHandler.getName());
        if (postInterceptor != null) {
            return postInterceptor;
        }
        synchronized (clazz2PostInterceptor) {
            postInterceptor = clazz2PostInterceptor.get(postInterceptorHandler.getName());
            if (postInterceptor != null) {
                return postInterceptor;
            }
            try {
                postInterceptor = postInterceptorHandler.newInstance();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            clazz2PostInterceptor.put(postInterceptorHandler.getName(), postInterceptor);
            return postInterceptor;
        }
    }
}
